package com.wu.project.admin.component;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.wu.project.admin.annotations.Log;
import com.wu.project.admin.entity.LogEntity;
import com.wu.project.admin.property.Constants;
import com.wu.project.admin.service.LogService;
import com.wu.project.admin.util.BaseUtil;
import com.wu.project.admin.util.CommonResultUtil;
import com.wu.project.admin.util.JwtUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author xuan
 */

@Aspect
@Component
public class LogAspect {

    Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Autowired
    LogService logService;

    @Pointcut("@annotation(com.wu.project.admin.annotations.Log)")
    public void logPoint() {
    }

    @Around("logPoint()")
    public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws JsonProcessingException {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();

        MethodSignature method = (MethodSignature) proceedingJoinPoint.getSignature();
        Log annotation = method.getMethod().getDeclaredAnnotation(Log.class);

        String token = request.getHeader("Authorized");
        String userName = "未知";
        try {
            userName = JwtUtil.Instance.getClaimByName(token, "userName").asString();
        } catch (Exception e) {
            logger.error("AOP 日志解析token异常:{}" + e.getMessage(), e);
        }

        if (annotation != null && annotation.isOpen()) {
            logService.saveCommonLog(getLogEntity(userName,
                    CommonResultUtil.Instance.getObjectMapper().writeValueAsString(getFieldsName(proceedingJoinPoint)),
                    annotation.name()));
        }

        try {
            return proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            logService.saveExceptionLog(getLogEntity(userName,
                    CommonResultUtil.Instance.getObjectMapper().writeValueAsString(getFieldsName(proceedingJoinPoint)),
                    annotation.name(), throwable));
            throwable.printStackTrace();
        }
        return null;
    }

    private LogEntity getLogEntity(Object userName, String params, String name) throws JsonProcessingException {
        LogEntity logEntity = new LogEntity();
        logEntity.setName(name);
        logEntity.setParams(params);
        logEntity.setType(Constants.REQUEST_TYPE);
        String info = "用户:" + userName + "执行:" + name;
        logEntity.setInfo(info);
        return logEntity;
    }

    private LogEntity getLogEntity(Object userName, String params, String name, Throwable throwable) throws JsonProcessingException {
        LogEntity logEntity = new LogEntity();
        logEntity.setName(name);
        logEntity.setParams(params);
        String stackTrace = BaseUtil.Instance.FormatStackTrace(throwable);
        String info = "用户:" + userName + "执行:" + name + ",发生异常:" + System.lineSeparator() + stackTrace;
        logEntity.setInfo(info);
        logEntity.setException(stackTrace);
        return logEntity;
    }


    private Map<String, Object> getFieldsName(ProceedingJoinPoint joinPoint) {
        // 参数值
        Object[] args = joinPoint.getArgs();
        ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String[] parameterNames = pnd.getParameterNames(method);
        Map<String, Object> paramMap = new HashMap<>(32);
        for (int i = 0; i < parameterNames.length; i++) {
            paramMap.put(parameterNames[i], args[i]);
        }
        return paramMap;
    }

}
